#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "View.h"

#include "Class.h"
#include "Error.h"
#include "Document.h"
#include "Menu.h"
#include "CmdNo.h"
#include "ClipBoard.h"
#include "PrintManager.h"
#include "Alert_e.h"
#include "Clipper.h"
#include "OrdColl.h"
#include "String.h"
#include "Data.h"
#include "FileDialog.h"
#include "Progress.h"

//---- View --------------------------------------------------------------------

NewAbstractMetaImpl(View, VObject, (TP(clippers), TP(nexthandler), TP(focus)));

View::View(EvtHandler *eh, Rectangle itsExtent, int id) : VObject(itsExtent, id)
{
	clippers= 0;
	ResetFlag(eVObjOpen|eVObjHFixed|eVObjVFixed);
	nexthandler= eh;
}

View::~View()
{
	if (clippers) {
		SeqCollection *tmp= clippers;
		clippers= 0;
		tmp->ForEach(VObject,Remove)(this);
		delete tmp;
	}
}

void View::DoObserve(int i, int c, void *v, Object *op)
{
	if (op == (PrintManager*) gPrintManager) {
		if (Width() != gPrintManager->GetViewRect().Width())
			SetWidth(gPrintManager->GetViewRect().Width());
	} else
		VObject::DoObserve(i, c, v, op);
}

Document *View::GetDocument()
{
	return (Document*) FindNextHandlerOfClass(Meta(Document));
}

void View::SetExtent(Point newExtent)
{
	if (GetExtent() != newExtent) {
		VObject::SetExtent(newExtent);
		if (clippers) {
			clippers->ForEach(EvtHandler,Control)(GetId(),cPartExtentChanged,this);
		}
	}
}

void View::ExtentChanged(VObject*)
{
	if (clippers) {
		clippers->ForEach(VObject,ExtentChanged)(this);
	}
}

void View::SetContainer(VObject *v)
{
	VObject::SetContainer(v);

	if (clippers == 0)
		clippers= new OrdCollection;
	if (!clippers->FindPtr(v))
		clippers->Add(v);
}

void View::ClearContainer(VObject *clipper)
{
	VObject::ClearContainer(clipper);
	if (clippers) {
		clippers->RemovePtr(clipper);
		if (clippers->Size() <= 0)
			SafeDelete(clippers);
	}
}

void View::CheckOpen()
{
	ResetFlag(eVObjOpen);
	if (clippers) {
		Iter next(clippers);
		VObject *fp;

		while (fp= (VObject*) next()) {
			if (fp->IsOpen()) {
				SetFlag(eVObjOpen);
				break;
			}
		}
	}
	if (IsOpen())
		Update();  // hack
}

void View::Open(bool)
{
	//if (mode)
	//    VObject::Open(mode);
	//else
		CheckOpen();
}

//---- drawing

void View::DrawAll(Rectangle r, bool)
{
	if (!gPrinting)
		Update();
	Draw(r);
	if (!TestFlag(eViewNoPrint))
		gPrintManager->ShowPageGrid(r, this);
}

void View::InvalidateRect(Rectangle r)
{
	if (clippers) {
		clippers->ForEach(VObject,InvalidateViewRect)(r);
	}
}

void View::ShowInAllClippers(FeedbackFunc of, Clipper *cl, Command *cmd,
                        const Point &ap, const Point &pp, bool to)
{
        if (clippers) {
                clippers->ForEach(Clipper,DrawInFocus)(of, cmd, ap, pp, to);
        }
}

void View::Update()
{
}

Rectangle View::GetViewedRect()
{
	if (focus)
		return focus->GetViewedRect();
	return gRect0;
}

//---- Printing

Rectangle View::NextPageBreak(int, const Rectangle &r)
{
	return r;
}

void View::PrintAdorn(const Rectangle &r, int)
{
	// GrPaintRect(r, ePatGrey12);
}

//---- Selection

void View::ShowSelection(bool, bool)
{
}

void View::ClearSelection(bool)
{
}

//---- scrolling

void View::CalcScrollStep(Point &delta, const Point &relOrigin, const Point &scroll)
{
}

void View::ConstrainScroll(Point*)
{
}

void View::RevealRect(Rectangle revealRect, Point minToSee)
{
	if (clippers) {
		clippers->ForEach(VObject,RevealRect)(revealRect, minToSee);
	}
}

void View::RevealAlign(Rectangle revealRect, VObjAlign al)
{
	if (clippers) {
		clippers->ForEach(VObject,RevealAlign)(revealRect, al);
	}
}

void View::Scroll(int mode, Point scroll, bool redraw)
{
	if (clippers) {
		clippers->ForEach(VObject,Scroll)(mode, scroll, redraw);
	}
}

//---- event handling

EvtHandler *View::GetNextHandler()
{
	return nexthandler;
}

void View::SetNextHandler(EvtHandler *eh)
{
	nexthandler= eh;
}

Command *View::Input(Point lp, Token &t, Clipper *vf)
{
	focus= vf;
	if (t.IsMouseButton() && !(t.Flags & eFlgButDown) && TestFlag(eVObjKbdFocus))
		SetFirstHandler(this);
	Command *cmd= VObject::Input(lp, t, vf);
	//Command *cmd= DispatchEvents(lp, t, vf);
	focus= 0;
	return cmd;
}

Command *View::DoCursorKeyCommand(EvtCursorDir d, Token t)
{
	if (focus)
		focus->Scroll(cPartScrollStep, t.CursorPoint());
	return VObject::DoCursorKeyCommand(d, t);
}

Command *View::DoFunctionKeyCommand(int code, Token t)
{
	if (focus) {
		switch (t.FunctionCode()) {
		case 28:    // end
			focus->Scroll(cPartScrollAbs, Point(0, 30000));
			break;
		case 22:    // home
			focus->Scroll(cPartScrollAbs, gPoint0);
			break;
		case 24:    // page up
			focus->Scroll(cPartScrollPage, Point(0, -1));
			break;
		case 30:    // page down
			focus->Scroll(cPartScrollPage, Point(0, 1));
			break;
		}
	}
	return VObject::DoFunctionKeyCommand(code, t);
}

//---- clipboard

Command *View::PasteData(Data*)
{
	return gNoChanges;
}

extern void PrepareTypes();
extern bool EndTypes(Data *data);

bool View::CanPaste(Data *data)
{
	if (data) {
		PrepareTypes();
		PasteData(data);
		return EndTypes(data);
	}
	return FALSE;
}

bool View::HasSelection()
{
	return FALSE;
}

bool View::IsReadOnly()
{
	return FALSE;
}

static FileDialog *importDialog;

Command *View::DoMenuCommand(int cmd)
{
	Command *cm;

	switch (cmd) {
	case cIMPORT:
		if (importDialog == 0)
			importDialog= new FileDialog;

		if (importDialog->ShowInWindow(eFDImport, GetWindow(), this) == cIdOk) {
			Data *data= importDialog->GetData();
			if (CanPaste(data)) {
				gProgress->Start(form("Pasting file: %s", data->ShortName()),
														(int)data->SizeHint());
				cm= PasteData(data);
				gProgress->Stop();
				return cm;
			}
			ShowAlert(eAlertCaution, "Can't paste file (%s)", data->Type().AsString());
		}
		return gNoChanges;

	case cCOPY:
	case cCUT:
		return gNoChanges;

	case cPASTE:
		cm= gClipBoard->PasteClipboard(this);
		if (cm == gNoChanges)
			ShowAlert(eAlertCaution, "Can't paste clipboard (%s)",
									gClipBoard->GetType()->Type().AsString());
		return cm;

	case cPRINT:
		Print();
		return gNoChanges;

	default:
		break;
	}
	return EvtHandler::DoMenuCommand(cmd);
}

void View::DoSetupMenu(Menu *menu)
{
	EvtHandler::DoSetupMenu(menu);
	if (menu->GetId() == cEDITMENU) {
		if (!IsReadOnly()) {
			VObject *paste= menu->FindItem(cPASTE);
			if (paste && gClipBoard->CanPaste(this))
				paste->Enable(TRUE, FALSE);
			if (HasSelection())
				menu->EnableItems(cCUT, cCOPY, 0);
			menu->EnableItem(cIMPORT);
		} else {
			if (HasSelection())
				menu->EnableItem(cCOPY);
		}
	}
}

void View::InspectorId(char *buf, int sz)
{
	Window *w= GetWindow();
	if (w && w->GetNextHandler()) {
		w->GetNextHandler()->InspectorId(buf, sz-6);
		strn0cpy(buf, form("in %s", buf), sz);
	} else
		VObject::InspectorId(buf, sz);
}

